#include <cstdio>
#include <gl/glew.h>
#include <gl/freeglut.h>

#include "read_text_file.h"

static GLint winWidth = 600, winHeight = 400;

void init()
{
	glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluOrtho2D(0.0, winWidth, 0.0, winHeight);

	printf("OpenGL version=%s\n", (char*)glGetString(GL_VERSION));
	printf("GLSL version: %s\n", (char*)glGetString(GL_SHADING_LANGUAGE_VERSION));

	// read source string from text file
	GLchar* vertSource, * fragSource;

	vertSource = readTextFile("simpleShader.vert");
	if (!vertSource) {
		fputs("Failed to read vertex shader\n", stderr);
		exit(EXIT_FAILURE);
	}

	fragSource = readTextFile("simpleShader.frag");
	if (!fragSource) {
		fputs("Failed to read fragment shader\n", stderr);
		exit(EXIT_FAILURE);
	}

	// create shader
	GLuint vertShader, fragShader;
	vertShader = glCreateShader(GL_VERTEX_SHADER);
	fragShader = glCreateShader(GL_FRAGMENT_SHADER);

	// bind source string to shader
	glShaderSource(vertShader, 1, (const GLchar**)&vertSource, NULL);
	glShaderSource(fragShader, 1, (const GLchar**)&fragSource, NULL);

	free(vertSource);
	free(fragSource);

	// compile shader
	glCompileShader(vertShader);
	glCompileShader(fragShader);

	// check if success to compile shaders
	GLint status;

	glGetShaderiv(vertShader, GL_COMPILE_STATUS, &status);
	if (status != GL_TRUE) {
		fputs("Error in vertex shader compilation\n", stderr);
		exit(EXIT_FAILURE);
	}

	glGetShaderiv(fragShader, GL_COMPILE_STATUS, &status);
	if (status != GL_TRUE) {
		fputs("Error in fragment shader compilation\n", stderr);
		exit(EXIT_FAILURE);
	}

	// link shader to program
	GLuint program;
	program = glCreateProgram();

	glAttachShader(program, vertShader);
	glAttachShader(program, fragShader);

	// link program
	glLinkProgram(program);

	// check if success to link
	glGetProgramiv(vertShader, GL_LINK_STATUS, &status);
	if (status != GL_TRUE) {
		fputs("Error when linking shader program\n", stderr);
		exit(EXIT_FAILURE);
	}

#if 1
	GLint length;
	GLsizei num;
	char* log;

	glGetShaderiv(vertShader, GL_INFO_LOG_LENGTH, &length);
	if (length > 0) {
		log = (char*)malloc(sizeof(char) * length);
		glGetShaderInfoLog(vertShader, length, &num, log);
		fprintf(stderr, "%s\n", log);
		free(log);
	}

	glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
	if (length > 0) {
		log = (char*)malloc(sizeof(char) * length);
		glGetProgramInfoLog(program, length, &num, log);
		fprintf(stderr, "%s\n", log);
		free(log);
	}
#endif
	// active a program before drawing the object
	glUseProgram(program);

	// freesze all shaders
	glUseProgram(0);

	// delete shader
	glDeleteShader(vertShader);
	glDeleteShader(fragShader);

	glDeleteProgram(program);

	// unbind shader
	glDetachShader(program, vertShader);
	glDetachShader(program, fragShader);

	GLint location;

	location = glGetUniformLocation(program, "variableName");


	GLint i;
	GLfloat f;

	glGetUniformiv(program, location, &i);
	glGetUniformfv(program, location, &f);

	GLfloat v1, v2, v3, v4;

	glUniform1f(location, v1);
	glUniform2f(location, v1, v2);
	glUniform3f(location, v1, v2, v3);
}

void display()
{
	glClear(GL_COLOR_BUFFER_BIT);

	glFlush();
}

void reshape(GLint w, GLint h)
{
	winWidth = w;
	winHeight = h;

	glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluOrtho2D(0.0, winWidth, 0.0, winHeight);
}

int main(int argc, char* argv[])
{
	glutInit(&argc, argv);

	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	glutInitWindowPosition(50, 100);
	glutInitWindowSize(winWidth, winHeight);
	glutCreateWindow("GLSL demos");

	init();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);

	glutMainLoop();

	return 0;
}
